import re
import json
import pickle
import base64

from datetime import datetime

from django.core.mail import send_mail
from django.contrib.auth import authenticate, login, logout

from django.views import View
from django.http import JsonResponse
from django_redis import get_redis_connection

from apps.goods.models import SKU
from apps.users.models import User
from celery_tasks.email.tasks import celery_send_email

from utils.views import LoginRequiredJSONMixin
from apps.users.utils import check_verify_token

# F用于一列直的比较或者修改

"""
需求分析： 根据页面的功能(从上到下,从左到右)，哪些功能需要和后端配合完成
如何确定 哪些功能需要和后端进行交互呢？？？
        1. 经验
        2. 关注类似网站相似功能
"""
"""
前端： 当用户输入 用户名，密码，确认密码，手机号，是否同意协议之后，点会击注册按钮，前端会发送axios请求
后端：
    请求：    接受请求
    业务逻辑：   验证表单入库 
    响应：      JSON {'code':0, 'errmsg':'ok'}
    路由：      POST  register/
    步骤：      
            1. 接收请求(POST ---- JSON)
            2. 获取数据
            3. 验证数据
            4. 数据入库
            5. 返回响应
"""


class UsermobileView(View):
    def get(self, request, mobile):
        if not re.match("^1[35678]\d{9}$", mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号格式错误'})
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


class UsernameCountView(View):
    """ 用户名字重复判断API """

    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        return JsonResponse({'code': 0, 'count': count, 'errmsg': 'ok'})


class RegisterView(View):
    def post(self, request):

        body_bytes = request.body
        body_str = body_bytes.decode()
        body_dict = json.loads(body_str)
        # 2.获取数据
        """        
        username: this.username,
        password: this.password,
        password2: this.password2,
        mobile: this.mobile,
        sms_code: this.sms_code,
        allow: this.allow
        """

        username = body_dict.get("username")
        password = body_dict.get('password')
        password2 = body_dict.get('password2')
        mobile = body_dict.get('mobile')
        sms_code = body_dict.get('sms_code')
        allow = body_dict.get('allow')
        # all中有一个空值就为False
        if not all([username, password2, password, mobile, allow, sms_code]):
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})
        if not re.match('[a-zA-Z_-]{5,20}', username):
            return JsonResponse({'code': 400, 'errmsg': '用户名不符合要求'})
        if password2 != password:
            return JsonResponse({'code': 400, 'errmsg': '两次密码输入不一致'})
        if not re.match("^1[35678]\d{9}$", mobile):
            return JsonResponse({'code': 400, 'errmsg': '手机号不符合规范'})
        if not allow:
            return JsonResponse({'code': 400, 'errmsg': '请同意用户协议'})
        if User.objects.filter(username=username).count():
            return JsonResponse({'code': 400, 'errmsg': '用户名有重复'})
        if User.objects.filter(mobile=mobile).count():
            return JsonResponse({'code': 400, 'errmsg': '手机号已被注册'})
        # 连接数据库校验密码
        redis_cli = get_redis_connection('code')
        # 短信验证码判断
        code = redis_cli.get(mobile)
        if code is None:
            return JsonResponse({'code': '400', 'errmsg': '短信验证码已经过期'})
        if code.decode() != sms_code:
            return JsonResponse({'code': '400', 'errmsg': '短信验证码输入错误'})
        user = User.objects.create_user(username=username, password=password, mobile=mobile)
        login(request, user)
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        response.set_cookie('username', username, expires=3600 * 24)
        return response


class LoginView(View):
    """
    1. 接收数据
    2. 验证数据
    3. 是否要加入session
    4. 返回相应
    """

    def post(self, request):
        json_str = request.body.decode()
        req_data = json.loads(json_str)
        password = req_data.get('password')
        username = req_data.get('username')
        remembered = req_data.get('remembered')
        # 参数是否都存在
        if not all([password, username]):
            return JsonResponse({'code': 400, 'errmsg': '参数不全'})

        # 判断用户名和密码，使用django内置库 from django.contrib.auth import authenticate
        user = authenticate(username=username, password=password)
        user = authenticate(mobile=username, password=password) if not user else user
        if not user:
            return JsonResponse({'code': 400, 'errmsg': '帐号或者密码错误'})
        if remembered:
            request.session.set_expiry(60 * 60 * 24 * 4)
        else:
            request.session.set_expiry(0)
        # 为了减少首页请求次数， 设置COOKIE的user信息，在首页展示
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        login(request, user)
        response.set_cookie('username', username, max_age=60 * 60 * 24 * 4)

        # 登录成功后需要合并购物车的任务
        cookie_carts = request.COOKIES.get('carts')
        if cookie_carts is not None:
            carts = pickle.loads(base64.b64decode(cookie_carts))
        else:
            carts = {}
        cookie_dict = {}
        selected_ids = []
        unselected_ids = []
        for sku_id in carts:
            # carts[sku_id]是取得 {'count': x, 'selected': True}
            cookie_dict[sku_id] = carts[sku_id]['count']
            if carts[sku_id]['selected']:
                selected_ids.append(sku_id)
            else:
                unselected_ids.append(sku_id)
        response.delete_cookie('carts')
        if cookie_dict:
            # 连接redis
            redis_cli = get_redis_connection('carts')
            pipe = redis_cli.pipeline()
            pipe.hmset('carts_%s' % user.id, cookie_dict)
            pipe.execute()
        if len(selected_ids) > 0:
            pipe.sadd('select_%s' % user.id, *selected_ids)
        if len(unselected_ids) > 0:
            pipe.srem('selected_s%' % user.id, *unselected_ids)
        return response

"""
退出功能的实现
1. 删除session
2. 删除cookies
3. 重定向
4. 返回json数据
"""


class LogoutView(View):
    def get(self, request):
        # 清除session, 和指向session的cookies
        logout(request)
        # 清除自定义cookies
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        response.delete_cookie('username')
        return response


"""
LoginRequiredMixin未登录用户返回的是重定向 ---目的是只能让已经登录的用户访问视图
我们需要返回JSON数据让前端进行重定向
已经登录的话將数据
"""
""" 重写LoginRequiredMixin的方法 """


class CenterView(LoginRequiredJSONMixin, View):
    def get(self, request):
        info_data = {
            'username': request.user.username,
            'email': request.user.email,
            'email': request.user.email_activate,
        }
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'info_data': info_data})


class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        json_str = request.body
        json_str = json_str.decode()
        resp_data = json.loads(json_str)
        email = resp_data.get('email')
        if re.match('[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}', email) is None:
            return JsonResponse({'code': 400, 'errmsg': '邮箱格式错误'})
        user = request.user
        user.email = email
        celery_send_email.delay(request.user.id, email)
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


class EmailVerifyView(View):
    """ 验证邮箱， 获取token， 验证参数 """

    def put(self, request):
        params = request.GET
        # 获取token
        token = params.get('token')
        if token is None:
            return JsonResponse({'code': 400, 'errmsg': '参数缺失'})
        # 解密token
        user_id = check_verify_token(token)
        if user_id is None:
            return JsonResponse({'code': 400, 'errmsg': '参数错误'})
        # 查询数据
        user = User.objects.get(id=user_id)
        user.email_activate = True
        user.save()
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


class AlterPasswordView(LoginRequiredJSONMixin, View):
    def put(self, request):
        json_data = request.body.decode()
        data = json.loads(json_data)
        old_password = data.get('old_password')
        username = request.COOKIES.get('username')
        # 判断密码是否正确
        user = authenticate(username=username, password=old_password)
        if user is None:
            return JsonResponse({'code': 400, 'errmsg': '密码错误'})
        new_password = data.get('new_password')
        new_password1 = data.get('new_password')
        if new_password1 != new_password:
            return JsonResponse({'code': 400, 'errmsg': '密码两次输入不一致'})
        user.set_password(new_password)
        user.save()
        # 修改完成退出登录，清除cookies
        logout(request)
        response = JsonResponse({'code': 0, 'errmsg': 'ok'})
        response.delete_cookie('username')
        return response


"""
1. 最近浏览记录，只有登录用户才可以访问。我们只记录登录用户的浏览记录
2. 浏览记录因该有顺序
3. 没有分页
-|
 |_ 1.在用户访问商品的详情的时候，添加浏览记录
 |_ 2.在个人中心，展示浏览记录
 |___保存那些数据？用户ID
   |_保存在哪里？一般要保存在数据库中（缺点： 查询慢频繁操作数据库）
   |_最好保存在redis中
都可以。看公司具体安排。服务器内存比较大。 mysql+redis
需要 user_id, sku_id, 顺序
"""
"""
#  前端： 当登录用户访问某一个具体SKU页面的时候，发送一个axios请求，请求携带 sku_id
#  后端： 
        # 请求： 接受请求，获取参数， 验证参数,
        # 业务逻辑： 连接redis，保存到redis中
        # 响应： 返回JSON
        # 路由： POST
        # 步骤： 
            1. 接受参宿
            2. 获取请求参数
            3. 验证参数
            4. 连接redis
            5. 去重
            7. 只保存5条记录
展示浏览记录
"""


class UserHistoryView(View):
    def get(self, request):
        redis_cli = get_redis_connection('history')
        history = redis_cli.zrevrange('history_%s' % request.user.id, 0, -1)
        data = []
        # 拿到商品ID, 查询商品数据
        for sku_id in history:
            sku = SKU.objects.get(id=sku_id)
            data.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
            })
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'skus': data})

    def post(self, request):
        user = request.user
        # 5. 去重
        # 6. 只保存5条记录
        # 1. 接受参宿
        try:
            # 2. 获取请求参数
            # 3. 验证参数
            data = json.loads(request.body.decode())
            sku_id = data.get('sku_id')
            sku = SKU.objects.get(id=sku_id)
        except Exception as e:
            return JsonResponse({"code": 400, "errmsg": "没有该商品"})
        # 4. 连接redis
        redis_cli = get_redis_connection('history')
        redis_cli.zadd('history_%s' % user.id, {sku_id: int(datetime.now().timestamp())})
        # 5. 只需要保存五条记录
        if redis_cli.zcard('history_%s' % user.id) > 5:
            redis_cli.zremrangebyrank('history_%s' % user.id, 0, 0)  # 删除分数最低的元素
        return JsonResponse({'code': 0, 'errmsg': 'ok'})
